#ifndef K_SOCK_ADDRESS_H
#define K_SOCK_ADDRESS_H
#pragma once

#include "K_Winsock.h"
#include "KGlobal.h"
#include <string>

class KIAddress {
public:
    virtual ~KIAddress() = 0;
public:
    virtual bool isNull() const { return true; }
    virtual SOCKADDR *name() const = 0;
    virtual size_t length() const = 0;
};

class KHostAddress : public KIAddress {
public:
    KHostAddress(PCTSTR host, PCTSTR port, int flags, int family, int sockType, int protocol = IPPROTO_IP);
    virtual ~KHostAddress();
public:
    bool        isNull() const  { return !name() || length() < sizeof(SOCKADDR_IN); }
    SOCKADDR *  name() const    { return (next == NULL) ? NULL : next->ai_addr; }
    size_t      length() const  { return (next == NULL) ? 0 : next->ai_addrlen; }
public:
    bool        moveNext()      { return (next = next->ai_next) != NULL; }
private:
    ADDRINFOT * head;
    ADDRINFOT * next;
};

class KTcpHostAddress : public KHostAddress {
public:
    KTcpHostAddress(PCTSTR host, PCTSTR port, int flags = 0)
        : KHostAddress(host, port, flags, AF_INET, SOCK_STREAM, IPPROTO_TCP) {}
};

class KUdpHostAddress : public KHostAddress {
public:
    KUdpHostAddress(PCTSTR host, PCTSTR port, int flags = 0)
        : KHostAddress(host, port, flags, AF_INET, SOCK_DGRAM, IPPROTO_UDP) {}
};

class KIpAddress : public KIAddress {
public:
    enum Special { Any, Broadcast, LocalHost };
public:
    KIpAddress()                                                { reset(); }
    KIpAddress(u_short netPort, u_long netIp)                   { reset(netPort, netIp); }
    KIpAddress(u_short port, const char *ip)                    { reset(port, ip); }
    explicit KIpAddress(u_short port, KIpAddress::Special ip = KIpAddress::Any);
    explicit KIpAddress(const KIAddress &host)                  { reset(host); }
    explicit KIpAddress(const SOCKADDR_IN &addrIn)              { reset(addrIn); }
public:
    bool                isNull() const                          { return false; }
    std::wstring        hostName() const;
    const char *        ip() const                              { return inet_ntoa(m_addrIn.sin_addr); }
    u_long              hostIp() const                          { return ntohl(netIp()); }
    u_long              netIp() const                           { return m_addrIn.sin_addr.s_addr; }
    u_short             port() const                            { return ntohs(netPort()); }
    u_short             netPort() const                         { return m_addrIn.sin_port; }
    SOCKADDR *          name() const                            { return reinterpret_cast<SOCKADDR *>(&m_addrIn); }
    size_t              length() const                          { return sizeof m_addrIn; }
    LPINT               pLength() const                         { m_addrLen = length(); return &m_addrLen; }
public:
    void                reset()                                 { memset(&m_addrIn, 0, sizeof m_addrIn); }
    void                reset(u_short netPort, u_long netIp);
    void                reset(u_short port, const char *ip);
    void                reset(const KIAddress &host);
    void                reset(const SOCKADDR_IN &addrIn)        { m_addrIn = addrIn; }
private:
    mutable INT         m_addrLen;
    mutable SOCKADDR_IN m_addrIn;
};

#endif // K_SOCK_ADDRESS_H